#pragma once
#include "CTokens.h"
#include "CParser.h"

#define IDENTIFIERS_PER_LINE	64

class CDecompiler : public CParser
{
	static FILE					*	s_hInFile;
	static FILE					*	s_hOutFile;
	static bool						s_bHexOpen;
	static bool						s_bOpcodeNotMask;
	static bool						s_bIsInIF;
	static DWORD					s_nNumBytesRead;
	static int						s_nNumModels;
	static int						s_nNumMissions;
	static int						s_nNumStreamedScripts;
	static int						s_nMainSize;
	static int						s_nNumOutputLines;
	static int						s_nInputBufferSize;
	static int						s_nVarSpaceSize;
	static char						s_nFileTargetGame;
	static BYTE					*	s_pInputBuffer;
	static BYTE					*	s_pBuffer;
	static COperation			*	s_pOperation;
	static COpcode				*	s_pOpcode;
	static DWORD				*	s_dwModelIDs;
	static DWORD				*	s_dwMissionOffsets;
	static const CConstant<int>	**	s_Models;
	static CVariable			**	s_Variables;

	static CStreamedScriptInfo * s_StreamedScriptInfo;

	static void Init(FILE * hInFile, FILE *hOutFile)
	{
		InitBuffer();
		s_bHexOpen = false;
		s_nNumBytesRead = 0;
		s_nNumOutputLines = 0;
		s_hInFile = hInFile;
		s_hOutFile = hOutFile;
	}

	static void InitBuffer()
	{
		s_nInputBufferSize = SCRambl.Config.Performance.GetInputBufferSize();
		s_pInputBuffer = new BYTE[s_nInputBufferSize];
		s_pBuffer = s_pInputBuffer;
		ClearBuffer();
	}

	static void ClearBuffer()
	{
		memset(s_pInputBuffer, 0, s_nInputBufferSize);
	}

	static void FreeBuffer()
	{
		delete[] s_pInputBuffer;
	}

	static bool IsEndOfBuffer()
	{
		return s_pBuffer > s_pInputBuffer + s_nInputBufferSize;
	}

	static tOpcode ReadOpcode()					{return ReadBuffer<tOpcode>();}
	
	static void ReadString(char *pDest, size_t nSize)
	{
		if(s_pBuffer + nSize > s_pInputBuffer + s_nInputBufferSize)
		{
			if(!LoadBuffer()) return;
		}

		strncpy(pDest, (char*)s_pBuffer, nSize);
		s_pBuffer += nSize;
		s_nNumBytesRead += nSize;
	}

template<typename T>
	static inline T Read()								{return ReadBuffer<T>();}

template<typename T>
	static inline T GetValue(int dt)
	{
		/*switch(dt)
		{
		case DT_BYTE: return (T)Read<char>();
		case DT_WORD: return (T)Read<short>();
		case DT_DWORD: return (T)Read<int>();
		case DT_FLOAT: return (T)Read<float>();
		}*/
		return 0;
	}
	
	static COpcode * GetOpcode()
	{
		short opcode = ReadOpcode();
		
		if(opcode & LOGICAL_NOT)
		{
			s_bOpcodeNotMask = true;
			opcode ^= LOGICAL_NOT;
		}
		if(COpcode *pOpcode = COpcodes::Get(opcode))
			return pOpcode;

		CToken * pToken = RegisterToken(TOKEN_HEX_WORD);
		pToken->SetValue(opcode);
		return nullptr;
	}

	static void Error(const char *szStr)		{Printf("DECOMPILE ERROR: %s", szStr);}
	static void Warning(const char *szStr)		{Printf("DECOMPILE WARNING: %s", szStr);}

public:
	static int WriteOutput(FILE *);
	//static void WriteParams(CToken *pCommandToken);
	//static void WriteParam(CParamType &pt, CToken *pValToken);
	static int Decompile(FILE *, FILE *);
	static void Decode();
	static void DecodeHeader();
	static DWORD DecodeHeaderJump();
	static void DecodeCommand();
	static void DecodeOperation();
	static void DecodeOperand(bool);
	static void DecodeParam(const CTypeBase*, int dt, int nParam);
	static void DecodeVarParam(bool, bool);
	static bool LoadBuffer();
	static void SkipTo(DWORD);

template<typename T>
	static T ReadBuffer();
};